package framework.web.rpc.servlet;

import java.io.BufferedReader;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import framework.web.rpc.JsonRpcBridge;
import framework.web.rpc.JsonRpcRegister;
import framework.web.rpc.adapter.IAdapter;
import framework.web.rpc.secure.IRpcSecure;

/**
 * 1. 缓冲类或是类实例的过程应该是有系统启动时进行，而不是动态的，或是手工进行增加调整之类的操作
 * 2. 在这里处理的，就是从sevice中得到传进来的数据，然后reflect到具体的动作上去，并返回结果，这个结果，可以考虑ExtjsAdapter?
 * 请求数据格式: service/class_key/method/params
 * 	{"s":"", "c":"", "m":"", "p":[]}
 * 
 * @version $Revision$ $Date$
 * @author Base
 * @since 1.0
 */
public class JsonRpcServlet extends HttpServlet {
	private static final long	serialVersionUID	= -2297302870578293449L;

	private final static int	buf_size			= 4096;
	private String				charset				= "UTF-8";

	/** 需要搜索查找rpc对象的包路径 */
	private String				packages			= null;
	/** 如果有权限控制，则在这里提供实现了ISecureCheck接口的类 */
	private String				secure				= null;
	/** Json输出转换格式 */
	private String				adapter				= null;

	private IRpcSecure			check				= null;
	private IAdapter			oAdapter			= null;

	public void init(ServletConfig config) throws ServletException {
		super.init(config);

		packages = config.getInitParameter("packages");
		secure = config.getInitParameter("secure");
		adapter = config.getInitParameter("adapter");

		if (null != secure && secure.trim().length() > 0) {
			try {
				check = (IRpcSecure) Class.forName(secure).newInstance();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if (null != adapter && adapter.trim().length() > 0) {
			try {
				oAdapter = (IAdapter) Class.forName(adapter).newInstance();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/** 第一次调用时做初始化处理 */
	public boolean	bInit	= false;

	public void searchAllClass(HttpServletRequest request) {
		String[] ps = packages.split(";");
		for (String p : ps) {
			try {
				Class[] cc = getClasses(p);
				for (Class c : cc) {
					if (c.getSuperclass().getName().endsWith("JsonRpcObject")) {
						// TODO: ________-debug
						System.out.println("[RPCExt] " + c.getName().substring(c.getName().lastIndexOf(".") + 1) + "="
								+ c.getName());
						JsonRpcRegister.register(c.getName().substring(c.getName().lastIndexOf(".") + 1),
								c.newInstance());
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public void service(HttpServletRequest request, HttpServletResponse response) throws IOException,
			ClassCastException {
		HttpSession session = request.getSession(false);
		if (null == session)
			session = request.getSession(true);
		if (null != session) {
			if (!bInit)
				searchAllClass(request);
			bInit = true;

			String json = getData(request);
			System.out.println("RPC_IN>>>>>>>>>>>>>>" + json);
			// 这里处理权限相关的事情，需要有bridge实例，以取得对象路径
			Object o = JsonRpcBridge.getObject(json);
			if (check != null && !check.secureCheck(request, o)) // 即这里将时行权限管制  Object o = brg.getObjectByParams(szData);
				return;
			if (null != json && 0 < json.length()) {
				response.setContentType("text/plain;charset=" + charset);
				OutputStream out = response.getOutputStream();

				byte[] bout = null;
				String szOut = null;
				szOut = JsonRpcBridge.exec(request, json, oAdapter);
				System.out.println("RPC_OUT>>>>>>>>>>>>>>" + szOut);
				bout = szOut.getBytes("UTF-8");
				response.setIntHeader("Content-Length", bout.length);

				out.write(bout);
				out.flush();
				out.close();
			}
		}
	}

	private String getData(HttpServletRequest request) {
		try {
			BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), charset));

			// 读取request中的JSON对象
			CharArrayWriter data = new CharArrayWriter();
			char buf[] = new char[buf_size];
			int ret;
			while ((ret = in.read(buf, 0, buf_size)) != -1)
				data.write(buf, 0, ret);
			String json = data.toString();
			if (json.startsWith("__p") || !json.startsWith("{")) {
				String v = URLDecoder.decode(json.split("=")[1], charset);
				json = !v.startsWith("{") ? null : v;
			}
			if (null == json || json.length() <= 0) { // TMD，这里又要转码，java有病不，后台全用utf-8多好
				String params = new String(request.getParameter("__p").getBytes("ISO-8859-1"), charset);
				if (params != null)
					json = params;
			}
			return json;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	//--------------------------------------------------------------------------------------------
	/**
	 * 取得包下所有类
	 * @param packageName
	 * @return
	 * @throws ClassNotFoundException
	 * @throws IOException
	 */
	private Class[] getClasses(String packageName) throws Exception {
		ClassLoader classLoader = getClass().getClassLoader();// Thread.currentThread().getContextClassLoader();
		assert classLoader != null;
		String path = "/" + packageName.replace('.', '/');
		Enumeration resources = classLoader.getResources(path);
		List<File> dirs = new ArrayList<File>();
		while (resources.hasMoreElements()) {
			URL resource = (URL) resources.nextElement();
			dirs.add(new File(URLDecoder.decode(resource.getFile(), "UTF-8")));
		}
		List<Class> classes = new ArrayList<Class>();
		for (int j = 0; j < dirs.size(); j++) {
			File directory = (File) dirs.get(j);
			classes.addAll(findClasses(directory, packageName));
		}
		Class[] cs = new Class[classes.size()];
		for (int i = 0; i < cs.length; i++)
			cs[i] = (Class) classes.get(i);
		return cs;
	}

	/**
	 * 
	 * @param directory
	 * @param packageName
	 * @return
	 * @throws ClassNotFoundException
	 */
	private List<Class> findClasses(File directory, String packageName) throws Exception {
		List<Class> classes = new ArrayList<Class>();
		if (!directory.exists()) {
			return classes;
		}
		File[] files = directory.listFiles();
		for (int i = 0; i < files.length; i++) {
			File file = files[i];
			if (file.isDirectory()) {
				if (-1 == file.getName().indexOf(".")) {
					classes.addAll(findClasses(file, packageName + "." + URLDecoder.decode(file.getName(), "UTF-8")));
				}
			} else if (file.getName().endsWith(".class")) {
				classes.add(Class.forName(packageName + '.'
						+ URLDecoder.decode(file.getName(), "UTF-8").substring(0, file.getName().length() - 6)));
			}
		}
		return classes;
	}
}
